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_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::SWidgetConfig> 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())},

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.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"));
@ -23,6 +24,7 @@ CPasswordInputField::CPasswordInputField(const Vector2D& viewport_, const std::u
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"));
@ -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}};
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);

View file

@ -40,7 +40,7 @@ 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;
@ -53,6 +53,9 @@ class CPasswordInputField : public IWidget {
float size = 0;
float spacing = 0;
int rounding = 0;
std::string textFormat = "";
SPreloadedAsset* textAsset = nullptr;
std::string textResourceID;
} dots;
struct {