From 71021cc3de1b651aacb85c283aa991b78bfc4278 Mon Sep 17 00:00:00 2001 From: Maximilian Seidler <78690852+PaideiaDilemma@users.noreply.github.com> Date: Sat, 12 Oct 2024 23:16:31 +0000 Subject: [PATCH] input-field: add dots_text_format to support setting arbitrary chars as the input indicator (#510) * input-field: make dots support arbitrary chars * input-field: add font_familiy for placeholder and text dots * input-field: allow dots_spacing from -1.0 to 1.0 Useful when using emojis in dots_text_format --- src/config/ConfigManager.cpp | 4 ++ src/renderer/widgets/PasswordInputField.cpp | 62 ++++++++++++++++----- src/renderer/widgets/PasswordInputField.hpp | 17 +++--- 3 files changed, 63 insertions(+), 20 deletions(-) diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 5a06935..baabebb 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -106,9 +106,11 @@ void CConfigManager::init() { m_config.addSpecialConfigValue("input-field", "dots_spacing", Hyprlang::FLOAT{0.2}); m_config.addSpecialConfigValue("input-field", "dots_rounding", Hyprlang::INT{-1}); m_config.addSpecialConfigValue("input-field", "dots_fade_time", Hyprlang::INT{200}); + m_config.addSpecialConfigValue("input-field", "dots_text_format", Hyprlang::STRING{""}); m_config.addSpecialConfigValue("input-field", "fade_on_empty", Hyprlang::INT{1}); m_config.addSpecialConfigValue("input-field", "fade_timeout", Hyprlang::INT{2000}); m_config.addSpecialConfigValue("input-field", "font_color", Hyprlang::INT{0xFF000000}); + 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, -20}); @@ -260,9 +262,11 @@ std::vector CConfigManager::getWidgetConfigs() { {"dots_center", m_config.getSpecialConfigValue("input-field", "dots_center", k.c_str())}, {"dots_rounding", m_config.getSpecialConfigValue("input-field", "dots_rounding", k.c_str())}, {"dots_fade_time", m_config.getSpecialConfigValue("input-field", "dots_fade_time", k.c_str())}, + {"dots_text_format", m_config.getSpecialConfigValue("input-field", "dots_text_format", k.c_str())}, {"fade_on_empty", m_config.getSpecialConfigValue("input-field", "fade_on_empty", k.c_str())}, {"fade_timeout", m_config.getSpecialConfigValue("input-field", "fade_timeout", k.c_str())}, {"font_color", m_config.getSpecialConfigValue("input-field", "font_color", k.c_str())}, + {"font_family", m_config.getSpecialConfigValue("input-field", "font_family", k.c_str())}, {"halign", m_config.getSpecialConfigValue("input-field", "halign", k.c_str())}, {"valign", m_config.getSpecialConfigValue("input-field", "valign", k.c_str())}, {"position", m_config.getSpecialConfigValue("input-field", "position", k.c_str())}, diff --git a/src/renderer/widgets/PasswordInputField.cpp b/src/renderer/widgets/PasswordInputField.cpp index 5783b36..3284b2e 100644 --- a/src/renderer/widgets/PasswordInputField.cpp +++ b/src/renderer/widgets/PasswordInputField.cpp @@ -16,6 +16,7 @@ CPasswordInputField::CPasswordInputField(const Vector2D& viewport_, const std::u 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")); @@ -23,6 +24,7 @@ CPasswordInputField::CPasswordInputField(const Vector2D& viewport_, const std::u 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")); @@ -46,7 +48,7 @@ CPasswordInputField::CPasswordInputField(const Vector2D& viewport_, const std::u pos = posFromHVAlign(viewport, size, pos, halign, valign); dots.size = std::clamp(dots.size, 0.2f, 0.8f); - dots.spacing = std::clamp(dots.spacing, 0.f, 1.f); + dots.spacing = std::clamp(dots.spacing, -1.f, 1.f); colorConfig.transitionMs = std::clamp(colorConfig.transitionMs, 0, 1000); colorConfig.both = colorConfig.both == -1 ? colorConfig.outer : colorConfig.both; @@ -57,6 +59,19 @@ CPasswordInputField::CPasswordInputField(const Vector2D& viewport_, const std::u colorState.outer = colorConfig.outer; colorState.font = colorConfig.font; + if (!dots.textFormat.empty()) { + dots.textResourceID = std::format("input:{}-{}", (uintptr_t)this, dots.textFormat); + CAsyncResourceGatherer::SPreloadRequest request; + request.id = dots.textResourceID; + request.asset = dots.textFormat; + request.type = CAsyncResourceGatherer::eTargetType::TARGET_TEXT; + request.props["font_family"] = fontFamily; + request.props["color"] = colorState.font; + request.props["font_size"] = (int)(std::nearbyint(size.y * dots.size * 0.5f) * 2.f); + + g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request); + } + // request the inital placeholder asset updatePlaceholder(); } @@ -226,27 +241,41 @@ bool CPasswordInputField::draw(const SRenderData& data) { g_pRenderer->renderRect(inputFieldBox, innerCol, rounding == -1 ? inputFieldBox.h / 2.0 : rounding - outThick); if (!hiddenInputState.enabled && !g_pHyprlock->m_bFadeStarted) { - const int PASS_SIZE = std::nearbyint(inputFieldBox.h * dots.size * 0.5f) * 2.f; - const int PASS_SPACING = std::floor(PASS_SIZE * dots.spacing); - const int DOT_PAD = (inputFieldBox.h - PASS_SIZE) / 2; + const int RECTPASSSIZE = std::nearbyint(inputFieldBox.h * dots.size * 0.5f) * 2.f; + Vector2D passSize{RECTPASSSIZE, RECTPASSSIZE}; + int passSpacing = std::floor(passSize.x * dots.spacing); + + if (!dots.textFormat.empty()) { + if (!dots.textAsset) + dots.textAsset = g_pRenderer->asyncResourceGatherer->getAssetByID(dots.textResourceID); + + if (!dots.textAsset) + forceReload = true; + else { + passSize = dots.textAsset->texture.m_vSize; + passSpacing = std::floor(passSize.x * dots.spacing); + } + } + + const int DOT_PAD = (inputFieldBox.h - passSize.y) / 2; const int DOT_AREA_WIDTH = inputFieldBox.w - DOT_PAD * 2; // avail width for dots - const int MAX_DOTS = std::round(DOT_AREA_WIDTH * 1.0 / (PASS_SIZE + PASS_SPACING)); // max amount of dots that can fit in the area + const int MAX_DOTS = std::round(DOT_AREA_WIDTH * 1.0 / (passSize.x + passSpacing)); // max amount of dots that can fit in the area const int DOT_FLOORED = std::floor(dots.currentAmount); const float DOT_ALPHA = fontCol.a; // Calculate the total width required for all dots including spaces between them - const int TOTAL_DOTS_WIDTH = (PASS_SIZE + PASS_SPACING) * dots.currentAmount - PASS_SPACING; + const int TOTAL_DOTS_WIDTH = (passSize.x + passSpacing) * dots.currentAmount - passSpacing; // Calculate starting x-position to ensure dots stay centered within the input field int xstart = dots.center ? (DOT_AREA_WIDTH - TOTAL_DOTS_WIDTH) / 2 + DOT_PAD : DOT_PAD; if (dots.currentAmount > MAX_DOTS) - xstart = (inputFieldBox.w + MAX_DOTS * (PASS_SIZE + PASS_SPACING) - PASS_SPACING - 2 * TOTAL_DOTS_WIDTH) / 2; + xstart = (inputFieldBox.w + MAX_DOTS * (passSize.x + passSpacing) - passSpacing - 2 * TOTAL_DOTS_WIDTH) / 2; if (dots.rounding == -1) - dots.rounding = PASS_SIZE / 2.0; + dots.rounding = passSize.x / 2.0; else if (dots.rounding == -2) - dots.rounding = rounding == -1 ? PASS_SIZE / 2.0 : rounding * dots.size; + dots.rounding = rounding == -1 ? passSize.x / 2.0 : rounding * dots.size; for (int i = 0; i < dots.currentAmount; ++i) { if (i < DOT_FLOORED - MAX_DOTS) @@ -260,9 +289,16 @@ bool CPasswordInputField::draw(const SRenderData& data) { } Vector2D dotPosition = - inputFieldBox.pos() + Vector2D{xstart + (int)inputFieldBox.w % 2 / 2.f + i * (PASS_SIZE + PASS_SPACING), inputFieldBox.h / 2.f - PASS_SIZE / 2.f}; - CBox box{dotPosition, Vector2D{PASS_SIZE, PASS_SIZE}}; - g_pRenderer->renderRect(box, fontCol, dots.rounding); + inputFieldBox.pos() + Vector2D{xstart + (int)inputFieldBox.w % 2 / 2.f + i * (passSize.x + passSpacing), inputFieldBox.h / 2.f - passSize.y / 2.f}; + CBox box{dotPosition, passSize}; + if (!dots.textFormat.empty()) { + if (!dots.textAsset) + break; + + g_pRenderer->renderTexture(box, dots.textAsset->texture, fontCol.a, dots.rounding); + } else { + g_pRenderer->renderRect(box, fontCol, dots.rounding); + } fontCol.a = DOT_ALPHA; } } @@ -338,7 +374,7 @@ void CPasswordInputField::updatePlaceholder() { request.id = placeholder.resourceID; request.asset = placeholder.currentText; request.type = CAsyncResourceGatherer::eTargetType::TARGET_TEXT; - request.props["font_family"] = std::string{"Sans"}; + request.props["font_family"] = fontFamily; request.props["color"] = colorState.font; request.props["font_size"] = (int)size.y / 4; g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request); diff --git a/src/renderer/widgets/PasswordInputField.hpp b/src/renderer/widgets/PasswordInputField.hpp index 2e7c520..0c2b04f 100644 --- a/src/renderer/widgets/PasswordInputField.hpp +++ b/src/renderer/widgets/PasswordInputField.hpp @@ -40,19 +40,22 @@ class CPasswordInputField : public IWidget { Vector2D configPos; Vector2D configSize; - std::string halign, valign, configFailText, outputStringPort, configPlaceholderText; + std::string halign, valign, configFailText, outputStringPort, configPlaceholderText, fontFamily; uint64_t configFailTimeoutMs = 2000; int outThick, rounding; struct { - float currentAmount = 0; - int fadeMs = 0; + float currentAmount = 0; + int fadeMs = 0; std::chrono::system_clock::time_point lastFrame; - bool center = false; - float size = 0; - float spacing = 0; - int rounding = 0; + bool center = false; + float size = 0; + float spacing = 0; + int rounding = 0; + std::string textFormat = ""; + SPreloadedAsset* textAsset = nullptr; + std::string textResourceID; } dots; struct {