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
This commit is contained in:
Maximilian Seidler 2024-10-12 23:16:31 +00:00 committed by GitHub
parent 7362ce3435
commit 71021cc3de
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 63 additions and 20 deletions

View file

@ -106,9 +106,11 @@ void CConfigManager::init() {
m_config.addSpecialConfigValue("input-field", "dots_spacing", Hyprlang::FLOAT{0.2}); 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_rounding", Hyprlang::INT{-1});
m_config.addSpecialConfigValue("input-field", "dots_fade_time", Hyprlang::INT{200}); 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_on_empty", Hyprlang::INT{1});
m_config.addSpecialConfigValue("input-field", "fade_timeout", Hyprlang::INT{2000}); 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_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", "halign", Hyprlang::STRING{"center"});
m_config.addSpecialConfigValue("input-field", "valign", Hyprlang::STRING{"center"}); m_config.addSpecialConfigValue("input-field", "valign", Hyprlang::STRING{"center"});
m_config.addSpecialConfigValue("input-field", "position", Hyprlang::VEC2{0, -20}); m_config.addSpecialConfigValue("input-field", "position", Hyprlang::VEC2{0, -20});
@ -260,9 +262,11 @@ std::vector<CConfigManager::SWidgetConfig> CConfigManager::getWidgetConfigs() {
{"dots_center", m_config.getSpecialConfigValue("input-field", "dots_center", k.c_str())}, {"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_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_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_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())}, {"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_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())}, {"halign", m_config.getSpecialConfigValue("input-field", "halign", k.c_str())},
{"valign", m_config.getSpecialConfigValue("input-field", "valign", k.c_str())}, {"valign", m_config.getSpecialConfigValue("input-field", "valign", k.c_str())},
{"position", m_config.getSpecialConfigValue("input-field", "position", k.c_str())}, {"position", m_config.getSpecialConfigValue("input-field", "position", k.c_str())},

View file

@ -16,6 +16,7 @@ CPasswordInputField::CPasswordInputField(const Vector2D& viewport_, const std::u
dots.center = std::any_cast<Hyprlang::INT>(props.at("dots_center")); dots.center = std::any_cast<Hyprlang::INT>(props.at("dots_center"));
dots.rounding = std::any_cast<Hyprlang::INT>(props.at("dots_rounding")); dots.rounding = std::any_cast<Hyprlang::INT>(props.at("dots_rounding"));
dots.fadeMs = std::any_cast<Hyprlang::INT>(props.at("dots_fade_time")); 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")); fadeOnEmpty = std::any_cast<Hyprlang::INT>(props.at("fade_on_empty"));
fadeTimeoutMs = std::any_cast<Hyprlang::INT>(props.at("fade_timeout")); fadeTimeoutMs = std::any_cast<Hyprlang::INT>(props.at("fade_timeout"));
hiddenInputState.enabled = std::any_cast<Hyprlang::INT>(props.at("hide_input")); hiddenInputState.enabled = std::any_cast<Hyprlang::INT>(props.at("hide_input"));
@ -23,6 +24,7 @@ CPasswordInputField::CPasswordInputField(const Vector2D& viewport_, const std::u
configPlaceholderText = std::any_cast<Hyprlang::STRING>(props.at("placeholder_text")); configPlaceholderText = std::any_cast<Hyprlang::STRING>(props.at("placeholder_text"));
configFailText = std::any_cast<Hyprlang::STRING>(props.at("fail_text")); configFailText = std::any_cast<Hyprlang::STRING>(props.at("fail_text"));
configFailTimeoutMs = std::any_cast<Hyprlang::INT>(props.at("fail_timeout")); 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.transitionMs = std::any_cast<Hyprlang::INT>(props.at("fail_transition"));
colorConfig.outer = std::any_cast<Hyprlang::INT>(props.at("outer_color")); colorConfig.outer = std::any_cast<Hyprlang::INT>(props.at("outer_color"));
colorConfig.inner = std::any_cast<Hyprlang::INT>(props.at("inner_color")); colorConfig.inner = std::any_cast<Hyprlang::INT>(props.at("inner_color"));
@ -46,7 +48,7 @@ CPasswordInputField::CPasswordInputField(const Vector2D& viewport_, const std::u
pos = posFromHVAlign(viewport, size, pos, halign, valign); pos = posFromHVAlign(viewport, size, pos, halign, valign);
dots.size = std::clamp(dots.size, 0.2f, 0.8f); 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.transitionMs = std::clamp(colorConfig.transitionMs, 0, 1000);
colorConfig.both = colorConfig.both == -1 ? colorConfig.outer : colorConfig.both; 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.outer = colorConfig.outer;
colorState.font = colorConfig.font; 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 // request the inital placeholder asset
updatePlaceholder(); updatePlaceholder();
} }
@ -226,27 +241,41 @@ bool CPasswordInputField::draw(const SRenderData& data) {
g_pRenderer->renderRect(inputFieldBox, innerCol, rounding == -1 ? inputFieldBox.h / 2.0 : rounding - outThick); g_pRenderer->renderRect(inputFieldBox, innerCol, rounding == -1 ? inputFieldBox.h / 2.0 : rounding - outThick);
if (!hiddenInputState.enabled && !g_pHyprlock->m_bFadeStarted) { if (!hiddenInputState.enabled && !g_pHyprlock->m_bFadeStarted) {
const int PASS_SIZE = std::nearbyint(inputFieldBox.h * dots.size * 0.5f) * 2.f; const int RECTPASSSIZE = std::nearbyint(inputFieldBox.h * dots.size * 0.5f) * 2.f;
const int PASS_SPACING = std::floor(PASS_SIZE * dots.spacing); Vector2D passSize{RECTPASSSIZE, RECTPASSSIZE};
const int DOT_PAD = (inputFieldBox.h - PASS_SIZE) / 2; 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 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 int DOT_FLOORED = std::floor(dots.currentAmount);
const float DOT_ALPHA = fontCol.a; const float DOT_ALPHA = fontCol.a;
// Calculate the total width required for all dots including spaces between them // 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 // 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; int xstart = dots.center ? (DOT_AREA_WIDTH - TOTAL_DOTS_WIDTH) / 2 + DOT_PAD : DOT_PAD;
if (dots.currentAmount > MAX_DOTS) 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) if (dots.rounding == -1)
dots.rounding = PASS_SIZE / 2.0; dots.rounding = passSize.x / 2.0;
else if (dots.rounding == -2) 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) { for (int i = 0; i < dots.currentAmount; ++i) {
if (i < DOT_FLOORED - MAX_DOTS) if (i < DOT_FLOORED - MAX_DOTS)
@ -260,9 +289,16 @@ bool CPasswordInputField::draw(const SRenderData& data) {
} }
Vector2D dotPosition = 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}; 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, Vector2D{PASS_SIZE, PASS_SIZE}}; CBox box{dotPosition, passSize};
g_pRenderer->renderRect(box, fontCol, dots.rounding); 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; fontCol.a = DOT_ALPHA;
} }
} }
@ -338,7 +374,7 @@ void CPasswordInputField::updatePlaceholder() {
request.id = placeholder.resourceID; request.id = placeholder.resourceID;
request.asset = placeholder.currentText; request.asset = placeholder.currentText;
request.type = CAsyncResourceGatherer::eTargetType::TARGET_TEXT; 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["color"] = colorState.font;
request.props["font_size"] = (int)size.y / 4; request.props["font_size"] = (int)size.y / 4;
g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request); g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request);

View file

@ -40,19 +40,22 @@ class CPasswordInputField : public IWidget {
Vector2D configPos; Vector2D configPos;
Vector2D configSize; Vector2D configSize;
std::string halign, valign, configFailText, outputStringPort, configPlaceholderText; std::string halign, valign, configFailText, outputStringPort, configPlaceholderText, fontFamily;
uint64_t configFailTimeoutMs = 2000; uint64_t configFailTimeoutMs = 2000;
int outThick, rounding; int outThick, rounding;
struct { struct {
float currentAmount = 0; float currentAmount = 0;
int fadeMs = 0; int fadeMs = 0;
std::chrono::system_clock::time_point lastFrame; std::chrono::system_clock::time_point lastFrame;
bool center = false; bool center = false;
float size = 0; float size = 0;
float spacing = 0; float spacing = 0;
int rounding = 0; int rounding = 0;
std::string textFormat = "";
SPreloadedAsset* textAsset = nullptr;
std::string textResourceID;
} dots; } dots;
struct { struct {