From 4fc133c96fa1ad2968cad44f8e9e9e923cd0381a Mon Sep 17 00:00:00 2001 From: Maximilian Seidler <78690852+PaideiaDilemma@users.noreply.github.com> Date: Wed, 6 Nov 2024 17:50:42 +0100 Subject: [PATCH] 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 --- src/config/ConfigDataValues.hpp | 53 +++++++++++++++ src/config/ConfigManager.cpp | 63 ++++++++++++++++-- src/helpers/MiscFunctions.hpp | 5 -- src/renderer/widgets/Image.cpp | 30 +++++---- src/renderer/widgets/Label.cpp | 37 +++++------ src/renderer/widgets/PasswordInputField.cpp | 74 +++++++++++---------- src/renderer/widgets/Shape.cpp | 28 +++++--- 7 files changed, 200 insertions(+), 90 deletions(-) create mode 100644 src/config/ConfigDataValues.hpp diff --git a/src/config/ConfigDataValues.hpp b/src/config/ConfigDataValues.hpp new file mode 100644 index 0000000..67e3dce --- /dev/null +++ b/src/config/ConfigDataValues.hpp @@ -0,0 +1,53 @@ +#pragma once +#include "../helpers/Log.hpp" +#include +#include +#include + +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(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; +}; diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index a2de4fe..013f5f3 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -1,12 +1,17 @@ #include "ConfigManager.hpp" #include "../helpers/MiscFunctions.hpp" #include "../helpers/Log.hpp" +#include "../config/ConfigDataValues.hpp" #include #include #include #include #include +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(*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{"Input Password"}); 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"}); diff --git a/src/helpers/MiscFunctions.hpp b/src/helpers/MiscFunctions.hpp index 50b4d66..13aa1b0 100644 --- a/src/helpers/MiscFunctions.hpp +++ b/src/helpers/MiscFunctions.hpp @@ -5,8 +5,3 @@ #include 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}; -}; diff --git a/src/renderer/widgets/Image.cpp b/src/renderer/widgets/Image.cpp index 91299f6..99befdf 100644 --- a/src/renderer/widgets/Image.cpp +++ b/src/renderer/widgets/Image.cpp @@ -2,7 +2,7 @@ #include "../Renderer.hpp" #include "../../core/hyprlock.hpp" #include "../../helpers/Log.hpp" -#include "../../helpers/MiscFunctions.hpp" +#include "../../config/ConfigDataValues.hpp" #include #include @@ -82,18 +82,24 @@ void CImage::plantTimer() { CImage::CImage(const Vector2D& viewport_, COutput* output_, const std::string& resourceID_, const std::unordered_map& props) : viewport(viewport_), resourceID(resourceID_), output(output_), shadow(this, props, viewport_) { - size = std::any_cast(props.at("size")); - rounding = std::any_cast(props.at("rounding")); - border = std::any_cast(props.at("border_size")); - color = std::any_cast(props.at("border_color")); - pos = Vector2DFromHyprlang(std::any_cast(props.at("position"))); - halign = std::any_cast(props.at("halign")); - valign = std::any_cast(props.at("valign")); - angle = std::any_cast(props.at("rotate")); + try { + size = std::any_cast(props.at("size")); + rounding = std::any_cast(props.at("rounding")); + border = std::any_cast(props.at("border_size")); + color = std::any_cast(props.at("border_color")); + pos = CLayoutValueData::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")); - path = std::any_cast(props.at("path")); - reloadTime = std::any_cast(props.at("reload_time")); - reloadCommand = std::any_cast(props.at("reload_cmd")); + path = std::any_cast(props.at("path")); + reloadTime = std::any_cast(props.at("reload_time")); + reloadCommand = std::any_cast(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); diff --git a/src/renderer/widgets/Label.cpp b/src/renderer/widgets/Label.cpp index 56a1855..46bcdff 100644 --- a/src/renderer/widgets/Label.cpp +++ b/src/renderer/widgets/Label.cpp @@ -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 #include @@ -73,7 +73,13 @@ void CLabel::plantTimer() { CLabel::CLabel(const Vector2D& viewport_, const std::unordered_map& props, const std::string& output) : outputStringPort(output), shadow(this, props, viewport_) { try { - labelPreFormat = std::any_cast(props.at("text")); + pos = CLayoutValueData::fromAnyPv(props.at("position"))->getAbsolute(viewport_); + labelPreFormat = std::any_cast(props.at("text")); + halign = std::any_cast(props.at("halign")); + valign = std::any_cast(props.at("valign")); + angle = std::any_cast(props.at("rotate")); + angle = angle * M_PI / 180.0; + std::string textAlign = std::any_cast(props.at("text_align")); std::string fontFamily = std::any_cast(props.at("font_family")); CColor labelColor = std::any_cast(props.at("color")); @@ -93,27 +99,18 @@ CLabel::CLabel(const Vector2D& viewport_, const std::unordered_mapasyncResourceGatherer->requestAsyncAssetPreload(request); - - pos = Vector2DFromHyprlang(std::any_cast(props.at("position"))); - configPos = pos; - - viewport = viewport_; - - halign = std::any_cast(props.at("halign")); - valign = std::any_cast(props.at("valign")); - - angle = std::any_cast(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) { diff --git a/src/renderer/widgets/PasswordInputField.cpp b/src/renderer/widgets/PasswordInputField.cpp index aed2010..7f81ae8 100644 --- a/src/renderer/widgets/PasswordInputField.cpp +++ b/src/renderer/widgets/PasswordInputField.cpp @@ -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 #include #include @@ -10,44 +11,47 @@ using namespace Hyprutils::String; CPasswordInputField::CPasswordInputField(const Vector2D& viewport_, const std::unordered_map& props, const std::string& output) : - outputStringPort(output), shadow(this, props, viewport_) { - size = Vector2DFromHyprlang(std::any_cast(props.at("size"))); - outThick = std::any_cast(props.at("outline_thickness")); - dots.size = std::any_cast(props.at("dots_size")); - dots.spacing = std::any_cast(props.at("dots_spacing")); - dots.center = std::any_cast(props.at("dots_center")); - dots.rounding = std::any_cast(props.at("dots_rounding")); - dots.fadeMs = std::any_cast(props.at("dots_fade_time")); - dots.textFormat = std::any_cast(props.at("dots_text_format")); - fadeOnEmpty = std::any_cast(props.at("fade_on_empty")); - fadeTimeoutMs = std::any_cast(props.at("fade_timeout")); - hiddenInputState.enabled = std::any_cast(props.at("hide_input")); - rounding = std::any_cast(props.at("rounding")); - configPlaceholderText = std::any_cast(props.at("placeholder_text")); - configFailText = std::any_cast(props.at("fail_text")); - configFailTimeoutMs = std::any_cast(props.at("fail_timeout")); - fontFamily = std::any_cast(props.at("font_family")); - colorConfig.transitionMs = std::any_cast(props.at("fail_transition")); - colorConfig.outer = std::any_cast(props.at("outer_color")); - colorConfig.inner = std::any_cast(props.at("inner_color")); - colorConfig.font = std::any_cast(props.at("font_color")); - colorConfig.fail = std::any_cast(props.at("fail_color")); - colorConfig.check = std::any_cast(props.at("check_color")); - colorConfig.both = std::any_cast(props.at("bothlock_color")); - colorConfig.caps = std::any_cast(props.at("capslock_color")); - colorConfig.num = std::any_cast(props.at("numlock_color")); - colorConfig.invertNum = std::any_cast(props.at("invert_numlock")); - colorConfig.swapFont = std::any_cast(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(props.at("halign")); + valign = std::any_cast(props.at("valign")); + outThick = std::any_cast(props.at("outline_thickness")); + dots.size = std::any_cast(props.at("dots_size")); + dots.spacing = std::any_cast(props.at("dots_spacing")); + dots.center = std::any_cast(props.at("dots_center")); + dots.rounding = std::any_cast(props.at("dots_rounding")); + dots.fadeMs = std::any_cast(props.at("dots_fade_time")); + dots.textFormat = std::any_cast(props.at("dots_text_format")); + fadeOnEmpty = std::any_cast(props.at("fade_on_empty")); + fadeTimeoutMs = std::any_cast(props.at("fade_timeout")); + hiddenInputState.enabled = std::any_cast(props.at("hide_input")); + rounding = std::any_cast(props.at("rounding")); + configPlaceholderText = std::any_cast(props.at("placeholder_text")); + configFailText = std::any_cast(props.at("fail_text")); + configFailTimeoutMs = std::any_cast(props.at("fail_timeout")); + fontFamily = std::any_cast(props.at("font_family")); + colorConfig.transitionMs = std::any_cast(props.at("fail_transition")); + colorConfig.outer = std::any_cast(props.at("outer_color")); + colorConfig.inner = std::any_cast(props.at("inner_color")); + colorConfig.font = std::any_cast(props.at("font_color")); + colorConfig.fail = std::any_cast(props.at("fail_color")); + colorConfig.check = std::any_cast(props.at("check_color")); + colorConfig.both = std::any_cast(props.at("bothlock_color")); + colorConfig.caps = std::any_cast(props.at("capslock_color")); + colorConfig.num = std::any_cast(props.at("numlock_color")); + colorConfig.invertNum = std::any_cast(props.at("invert_numlock")); + colorConfig.swapFont = std::any_cast(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(props.at("position")); - pos = {POS__.x, POS__.y}; configPos = pos; configSize = size; - halign = std::any_cast(props.at("halign")); - valign = std::any_cast(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); diff --git a/src/renderer/widgets/Shape.cpp b/src/renderer/widgets/Shape.cpp index 3377311..e45b74a 100644 --- a/src/renderer/widgets/Shape.cpp +++ b/src/renderer/widgets/Shape.cpp @@ -1,21 +1,27 @@ #include "Shape.hpp" #include "../Renderer.hpp" -#include "../../helpers/MiscFunctions.hpp" +#include "../../config/ConfigDataValues.hpp" #include #include CShape::CShape(const Vector2D& viewport_, const std::unordered_map& props) : shadow(this, props, viewport_) { - size = Vector2DFromHyprlang(std::any_cast(props.at("size"))); - rounding = std::any_cast(props.at("rounding")); - border = std::any_cast(props.at("border_size")); - color = std::any_cast(props.at("color")); - borderColor = std::any_cast(props.at("border_color")); - pos = Vector2DFromHyprlang(std::any_cast(props.at("position"))); - halign = std::any_cast(props.at("halign")); - valign = std::any_cast(props.at("valign")); - angle = std::any_cast(props.at("rotate")); - xray = std::any_cast(props.at("xray")); + try { + size = CLayoutValueData::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")); + borderColor = std::any_cast(props.at("border_color")); + pos = CLayoutValueData::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")); + xray = std::any_cast(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;