From 8a13e97ee01df72a9ad79f6d7c4de93284f2ca7d Mon Sep 17 00:00:00 2001 From: Maximilian Seidler Date: Thu, 19 Dec 2024 13:53:49 +0100 Subject: [PATCH] config: add sizex using CLayoutXValueData for reproducible layouting of image and label BREAKING: removed image:size in favour of image:sizex --- src/config/ConfigDataValues.hpp | 43 ++++++++++--- src/config/ConfigManager.cpp | 70 ++++++++++++++++----- src/renderer/widgets/IWidget.cpp | 2 +- src/renderer/widgets/Image.cpp | 18 +++--- src/renderer/widgets/Image.hpp | 2 +- src/renderer/widgets/Label.cpp | 18 ++++-- src/renderer/widgets/Label.hpp | 1 + src/renderer/widgets/PasswordInputField.cpp | 4 +- src/renderer/widgets/Shape.cpp | 4 +- 9 files changed, 120 insertions(+), 42 deletions(-) diff --git a/src/config/ConfigDataValues.hpp b/src/config/ConfigDataValues.hpp index 7e7e339..5d183a1 100644 --- a/src/config/ConfigDataValues.hpp +++ b/src/config/ConfigDataValues.hpp @@ -12,8 +12,9 @@ using namespace Hyprutils::String; enum eConfigValueDataTypes { CVD_TYPE_INVALID = -1, - CVD_TYPE_LAYOUT = 0, + CVD_TYPE_LAYOUTXY = 0, CVD_TYPE_GRADIENT = 1, + CVD_TYPE_LAYOUTX = 2, }; class ICustomConfigValueData { @@ -25,22 +26,22 @@ class ICustomConfigValueData { virtual std::string toString() = 0; }; -class CLayoutValueData : public ICustomConfigValueData { +class CLayoutXYValueData : public ICustomConfigValueData { public: - CLayoutValueData() {}; - virtual ~CLayoutValueData() {}; + CLayoutXYValueData() {}; + virtual ~CLayoutXYValueData() {}; virtual eConfigValueDataTypes getDataType() { - return CVD_TYPE_LAYOUT; + return CVD_TYPE_LAYOUTXY; } 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) { + static CLayoutXYValueData* fromAnyPv(const std::any& v) { RASSERT(v.type() == typeid(void*), "Invalid config value type"); - const auto P = (CLayoutValueData*)std::any_cast(v); + const auto P = (CLayoutXYValueData*)std::any_cast(v); RASSERT(P, "Empty config value"); return P; } @@ -59,6 +60,34 @@ class CLayoutValueData : public ICustomConfigValueData { } m_sIsRelative; }; +class CLayoutXValueData : public ICustomConfigValueData { + public: + CLayoutXValueData() {}; + virtual ~CLayoutXValueData() {}; + + virtual eConfigValueDataTypes getDataType() { + return CVD_TYPE_LAYOUTXY; + } + + virtual std::string toString() { + return std::format("{}{}", m_fValue, (m_sIsRelative) ? "%" : "px"); + } + + static CLayoutXValueData* fromAnyPv(const std::any& v) { + RASSERT(v.type() == typeid(void*), "Invalid config value type"); + const auto P = (CLayoutXValueData*)std::any_cast(v); + RASSERT(P, "Empty config value"); + return P; + } + + double getAbsolute(const Hyprutils::Math::Vector2D& viewport) { + return (m_sIsRelative) ? (m_fValue / 100) * viewport.x : m_fValue; + } + + double m_fValue = 0; + bool m_sIsRelative = false; +}; + class CGradientValueData : public ICustomConfigValueData { public: CGradientValueData() {}; diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 0c8f9ab..e8f7ef1 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -25,15 +25,15 @@ static Hyprlang::CParseResult handleSource(const char* c, const char* v) { return result; } -static Hyprlang::CParseResult configHandleLayoutOption(const char* v, void** data) { +static Hyprlang::CParseResult configHandleLayoutXYOption(const char* v, void** data) { const std::string VALUE = v; Hyprlang::CParseResult result; if (!*data) - *data = new CLayoutValueData(); + *data = new CLayoutXYValueData(); - const auto DATA = (CLayoutValueData*)(*data); + const auto DATA = (CLayoutXYValueData*)(*data); const auto SPLIT = VALUE.find(','); if (SPLIT == std::string::npos) { result.setError(std::format("expected two comma seperated values, got {}", VALUE).c_str()); @@ -65,9 +65,43 @@ static Hyprlang::CParseResult configHandleLayoutOption(const char* v, void** dat return result; } -static void configHandleLayoutOptionDestroy(void** data) { +static void configHandleLayoutXYOptionDestroy(void** data) { if (*data) - delete reinterpret_cast(*data); + delete reinterpret_cast(*data); +} + +static Hyprlang::CParseResult configHandleLayoutXOption(const char* VALUE, void** data) { + std::string V = VALUE; + + Hyprlang::CParseResult result; + + if (!*data) + *data = new CLayoutXValueData(); + + const auto DATA = reinterpret_cast(*data); + + if (V.ends_with("px")) { + V.pop_back(); + V.pop_back(); + } else if (V.ends_with("%")) { + V.pop_back(); + DATA->m_sIsRelative = true; + } + + try { + DATA->m_fValue = configStringToInt(V); + } catch (std::exception& e) { + Debug::log(WARN, "Error parsing layout size {}", V); + + result.setError(("Error parsing layout size " + V + ": " + e.what()).c_str()); + } + + return result; +} + +static void configHandleLayoutXOptionDestroy(void** data) { + if (*data) + delete reinterpret_cast(*data); } static Hyprlang::CParseResult configHandleGradientSet(const char* VALUE, void** data) { @@ -152,8 +186,12 @@ inline static constexpr auto GRADIENTCONFIG = [](const char* default_value) -> H return Hyprlang::CUSTOMTYPE{&configHandleGradientSet, configHandleGradientDestroy, default_value}; }; -inline static constexpr auto LAYOUTCONFIG = [](const char* default_value) -> Hyprlang::CUSTOMTYPE { - return Hyprlang::CUSTOMTYPE{&configHandleLayoutOption, configHandleLayoutOptionDestroy, default_value}; +inline static constexpr auto LAYOUTXYCONFIG = [](const char* default_value) -> Hyprlang::CUSTOMTYPE { + return Hyprlang::CUSTOMTYPE{&configHandleLayoutXYOption, configHandleLayoutXYOptionDestroy, default_value}; +}; + +inline static constexpr auto LAYOUTXCONFIG = [](const char* default_value) -> Hyprlang::CUSTOMTYPE { + return Hyprlang::CUSTOMTYPE{&configHandleLayoutXOption, configHandleLayoutXOptionDestroy, default_value}; }; void CConfigManager::init() { @@ -197,12 +235,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", LAYOUTCONFIG("100,100")); + m_config.addSpecialConfigValue("shape", "size", LAYOUTXYCONFIG("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", GRADIENTCONFIG("0xFF00CFE6")); m_config.addSpecialConfigValue("shape", "color", Hyprlang::INT{0xFF111111}); - m_config.addSpecialConfigValue("shape", "position", LAYOUTCONFIG("0,0")); + m_config.addSpecialConfigValue("shape", "position", LAYOUTXYCONFIG("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}); @@ -213,11 +251,11 @@ void CConfigManager::init() { m_config.addSpecialCategory("image", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true}); m_config.addSpecialConfigValue("image", "monitor", Hyprlang::STRING{""}); m_config.addSpecialConfigValue("image", "path", Hyprlang::STRING{""}); - m_config.addSpecialConfigValue("image", "size", Hyprlang::INT{150}); + m_config.addSpecialConfigValue("image", "sizex", LAYOUTXCONFIG("10%")); m_config.addSpecialConfigValue("image", "rounding", Hyprlang::INT{-1}); m_config.addSpecialConfigValue("image", "border_size", Hyprlang::INT{4}); m_config.addSpecialConfigValue("image", "border_color", GRADIENTCONFIG("0xFFDDDDDD")); - m_config.addSpecialConfigValue("image", "position", LAYOUTCONFIG("0,0")); + m_config.addSpecialConfigValue("image", "position", LAYOUTXYCONFIG("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}); @@ -228,7 +266,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", LAYOUTCONFIG("400,90")); + m_config.addSpecialConfigValue("input-field", "size", LAYOUTXYCONFIG("400,90")); m_config.addSpecialConfigValue("input-field", "inner_color", Hyprlang::INT{0xFFDDDDDD}); m_config.addSpecialConfigValue("input-field", "outer_color", GRADIENTCONFIG("0xFF111111")); m_config.addSpecialConfigValue("input-field", "outline_thickness", Hyprlang::INT{4}); @@ -244,7 +282,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", LAYOUTCONFIG("0,0")); + m_config.addSpecialConfigValue("input-field", "position", LAYOUTXYCONFIG("0,0")); m_config.addSpecialConfigValue("input-field", "placeholder_text", Hyprlang::STRING{"Input Password"}); m_config.addSpecialConfigValue("input-field", "hide_input", Hyprlang::INT{0}); m_config.addSpecialConfigValue("input-field", "rounding", Hyprlang::INT{-1}); @@ -263,9 +301,10 @@ 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", LAYOUTCONFIG("0,0")); + m_config.addSpecialConfigValue("label", "position", LAYOUTXYCONFIG("0,0")); m_config.addSpecialConfigValue("label", "color", Hyprlang::INT{0xFFFFFFFF}); m_config.addSpecialConfigValue("label", "font_size", Hyprlang::INT{16}); + m_config.addSpecialConfigValue("label", "sizex", LAYOUTXCONFIG("0")); m_config.addSpecialConfigValue("label", "text", Hyprlang::STRING{"Sample Text"}); m_config.addSpecialConfigValue("label", "font_family", Hyprlang::STRING{"Sans"}); m_config.addSpecialConfigValue("label", "halign", Hyprlang::STRING{"none"}); @@ -363,7 +402,7 @@ std::vector CConfigManager::getWidgetConfigs() { std::any_cast(m_config.getSpecialConfigValue("image", "monitor", k.c_str())), { {"path", m_config.getSpecialConfigValue("image", "path", k.c_str())}, - {"size", m_config.getSpecialConfigValue("image", "size", k.c_str())}, + {"sizex", m_config.getSpecialConfigValue("image", "sizex", k.c_str())}, {"rounding", m_config.getSpecialConfigValue("image", "rounding", k.c_str())}, {"border_size", m_config.getSpecialConfigValue("image", "border_size", k.c_str())}, {"border_color", m_config.getSpecialConfigValue("image", "border_color", k.c_str())}, @@ -434,6 +473,7 @@ std::vector CConfigManager::getWidgetConfigs() { {"position", m_config.getSpecialConfigValue("label", "position", k.c_str())}, {"color", m_config.getSpecialConfigValue("label", "color", k.c_str())}, {"font_size", m_config.getSpecialConfigValue("label", "font_size", k.c_str())}, + {"sizex", m_config.getSpecialConfigValue("label", "sizex", k.c_str())}, {"font_family", m_config.getSpecialConfigValue("label", "font_family", k.c_str())}, {"text", m_config.getSpecialConfigValue("label", "text", k.c_str())}, {"halign", m_config.getSpecialConfigValue("label", "halign", k.c_str())}, diff --git a/src/renderer/widgets/IWidget.cpp b/src/renderer/widgets/IWidget.cpp index e145868..0bfb630 100644 --- a/src/renderer/widgets/IWidget.cpp +++ b/src/renderer/widgets/IWidget.cpp @@ -53,7 +53,7 @@ Vector2D IWidget::posFromHVAlign(const Vector2D& viewport, const Vector2D& size, else if (valign != "none") Debug::log(ERR, "IWidget: invalid valign {}", valign); - return pos; + return pos.round(); } static void replaceAllAttempts(std::string& str) { diff --git a/src/renderer/widgets/Image.cpp b/src/renderer/widgets/Image.cpp index db531d1..f39f745 100644 --- a/src/renderer/widgets/Image.cpp +++ b/src/renderer/widgets/Image.cpp @@ -2,6 +2,7 @@ #include "../Renderer.hpp" #include "../../core/hyprlock.hpp" #include "../../helpers/Log.hpp" +#include "../../helpers/MiscFunctions.hpp" #include "../../config/ConfigDataValues.hpp" #include #include @@ -46,7 +47,7 @@ void CImage::onTimerUpdate() { } try { - const auto MTIME = std::filesystem::last_write_time(path); + const auto MTIME = std::filesystem::last_write_time(absolutePath(path, "/")); if (OLDPATH == path && MTIME == modificationTime) return; @@ -83,11 +84,11 @@ CImage::CImage(const Vector2D& viewport_, COutput* output_, const std::string& r viewport(viewport_), resourceID(resourceID_), output(output_), shadow(this, props, viewport_) { try { - size = std::any_cast(props.at("size")); + sizex = CLayoutXValueData::fromAnyPv(props.at("sizex"))->getAbsolute(viewport_); rounding = std::any_cast(props.at("rounding")); border = std::any_cast(props.at("border_size")); color = *CGradientValueData::fromAnyPv(props.at("border_color")); - pos = CLayoutValueData::fromAnyPv(props.at("position"))->getAbsolute(viewport_); + pos = CLayoutXYValueData::fromAnyPv(props.at("position"))->getAbsolute(viewport_); halign = std::any_cast(props.at("halign")); valign = std::any_cast(props.at("valign")); angle = std::any_cast(props.at("rotate")); @@ -102,7 +103,7 @@ CImage::CImage(const Vector2D& viewport_, COutput* output_, const std::string& r } try { - modificationTime = std::filesystem::last_write_time(path); + modificationTime = std::filesystem::last_write_time(absolutePath(path, "/")); } catch (std::exception& e) { Debug::log(ERR, "{}", e.what()); } angle = angle * M_PI / 180.0; @@ -132,14 +133,11 @@ bool CImage::draw(const SRenderData& data) { const Vector2D IMAGEPOS = {border, border}; const Vector2D BORDERPOS = {0.0, 0.0}; const Vector2D TEXSIZE = asset->texture.m_vSize; - const float SCALEX = size / TEXSIZE.x; - const float SCALEY = size / TEXSIZE.y; + const auto XYRATIO = asset->texture.m_vSize.y / asset->texture.m_vSize.x; + const Vector2D SIZE = {sizex, sizex * XYRATIO}; // image with borders offset, with extra pixel for anti-aliasing when rotated - CBox texbox = {angle == 0 ? IMAGEPOS : IMAGEPOS + Vector2D{1.0, 1.0}, TEXSIZE}; - - texbox.w *= std::max(SCALEX, SCALEY); - texbox.h *= std::max(SCALEX, SCALEY); + CBox texbox = {angle == 0 ? IMAGEPOS : IMAGEPOS + Vector2D{1.0, 1.0}, SIZE.round()}; const bool ALLOWROUND = rounding > -1 && rounding < std::min(texbox.w, texbox.h) / 2.0; diff --git a/src/renderer/widgets/Image.hpp b/src/renderer/widgets/Image.hpp index 1831efb..fbae53a 100644 --- a/src/renderer/widgets/Image.hpp +++ b/src/renderer/widgets/Image.hpp @@ -29,7 +29,7 @@ class CImage : public IWidget { private: CFramebuffer imageFB; - int size; + double sizex; int rounding; double border; double angle; diff --git a/src/renderer/widgets/Label.cpp b/src/renderer/widgets/Label.cpp index 46bcdff..3d9cbcb 100644 --- a/src/renderer/widgets/Label.cpp +++ b/src/renderer/widgets/Label.cpp @@ -5,6 +5,7 @@ #include "../../helpers/Color.hpp" #include "../../config/ConfigDataValues.hpp" #include +#include #include CLabel::~CLabel() { @@ -73,7 +74,8 @@ void CLabel::plantTimer() { CLabel::CLabel(const Vector2D& viewport_, const std::unordered_map& props, const std::string& output) : outputStringPort(output), shadow(this, props, viewport_) { try { - pos = CLayoutValueData::fromAnyPv(props.at("position"))->getAbsolute(viewport_); + pos = CLayoutXYValueData::fromAnyPv(props.at("position"))->getAbsolute(viewport_); + sizex = CLayoutXValueData::fromAnyPv(props.at("sizex"))->getAbsolute(viewport_); labelPreFormat = std::any_cast(props.at("text")); halign = std::any_cast(props.at("halign")); valign = std::any_cast(props.at("valign")); @@ -125,10 +127,18 @@ bool CLabel::draw(const SRenderData& data) { shadow.draw(data); - // calc pos - pos = posFromHVAlign(viewport, asset->texture.m_vSize, configPos, halign, valign, angle); + if (asset->texture.m_vSize.x == 0) + return false; - CBox box = {pos.x, pos.y, asset->texture.m_vSize.x, asset->texture.m_vSize.y}; + auto size = asset->texture.m_vSize; + if (sizex != 0) { + const auto XYRATIO = asset->texture.m_vSize.y / asset->texture.m_vSize.x; + size = {sizex, sizex * XYRATIO}; + } + // calc pos + pos = posFromHVAlign(viewport, size.round(), configPos, halign, valign, angle); + + CBox box = {pos, size}; box.rot = angle; g_pRenderer->renderTexture(box, asset->texture, data.opacity); diff --git a/src/renderer/widgets/Label.hpp b/src/renderer/widgets/Label.hpp index 0822218..35a1783 100644 --- a/src/renderer/widgets/Label.hpp +++ b/src/renderer/widgets/Label.hpp @@ -32,6 +32,7 @@ class CLabel : public IWidget { Vector2D viewport; Vector2D pos; Vector2D configPos; + double sizex; double angle; std::string resourceID; std::string pendingResourceID; // if dynamic label diff --git a/src/renderer/widgets/PasswordInputField.cpp b/src/renderer/widgets/PasswordInputField.cpp index f8b4be3..a2ac2c5 100644 --- a/src/renderer/widgets/PasswordInputField.cpp +++ b/src/renderer/widgets/PasswordInputField.cpp @@ -13,8 +13,8 @@ using namespace Hyprutils::String; CPasswordInputField::CPasswordInputField(const Vector2D& viewport_, const std::unordered_map& props, const std::string& output) : 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_); + pos = CLayoutXYValueData::fromAnyPv(props.at("position"))->getAbsolute(viewport_); + size = CLayoutXYValueData::fromAnyPv(props.at("size"))->getAbsolute(viewport_); halign = std::any_cast(props.at("halign")); valign = std::any_cast(props.at("valign")); outThick = std::any_cast(props.at("outline_thickness")); diff --git a/src/renderer/widgets/Shape.cpp b/src/renderer/widgets/Shape.cpp index 48394d0..a17813a 100644 --- a/src/renderer/widgets/Shape.cpp +++ b/src/renderer/widgets/Shape.cpp @@ -7,12 +7,12 @@ CShape::CShape(const Vector2D& viewport_, const std::unordered_map& props) : shadow(this, props, viewport_) { try { - size = CLayoutValueData::fromAnyPv(props.at("size"))->getAbsolute(viewport_); + size = CLayoutXYValueData::fromAnyPv(props.at("size"))->getAbsolute(viewport_); rounding = std::any_cast(props.at("rounding")); border = std::any_cast(props.at("border_size")); color = std::any_cast(props.at("color")); borderGrad = *CGradientValueData::fromAnyPv(props.at("border_color")); - pos = CLayoutValueData::fromAnyPv(props.at("position"))->getAbsolute(viewport_); + pos = CLayoutXYValueData::fromAnyPv(props.at("position"))->getAbsolute(viewport_); halign = std::any_cast(props.at("halign")); valign = std::any_cast(props.at("valign")); angle = std::any_cast(props.at("rotate"));