mirror of
https://github.com/hyprwm/hyprlock.git
synced 2024-12-21 21:09:49 +01:00
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:
parent
7362ce3435
commit
71021cc3de
3 changed files with 63 additions and 20 deletions
|
@ -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())},
|
||||
|
|
|
@ -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}};
|
||||
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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue