Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/config/ConfigManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ 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_text_format", Hyprlang::STRING{""});
m_config.addSpecialConfigValue("input-field", "dots_text_change", Hyprlang::INT{0});
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});
Expand Down Expand Up @@ -471,6 +472,7 @@ 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_text_format", m_config.getSpecialConfigValue("input-field", "dots_text_format", k.c_str())},
{"dots_text_change", m_config.getSpecialConfigValue("input-field", "dots_text_change", 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())},
Expand Down
114 changes: 108 additions & 6 deletions src/renderer/widgets/PasswordInputField.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,28 @@ void CPasswordInputField::configure(const std::unordered_map<std::string, std::a
RASSERT(false, "Missing property for CPasswordInputField: {}", e.what()); //
}

if (props.find("dots_text_change") != props.end()) {
dots.textChange = std::any_cast<Hyprlang::INT>(props.at("dots_text_change"));
} else {
dots.textChange = false;
}

if (!dots.textFormat.empty() && dots.textFormat.front() == '[' && dots.textFormat.back() == ']') {
std::string chars = dots.textFormat.substr(1, dots.textFormat.size() - 2);
size_t i = 0;
while (i < chars.size()) {
size_t len = 1;
unsigned char c = static_cast<unsigned char>(chars[i]);
if (c < 0x80) len = 1;
else if ((c & 0xE0) == 0xC0) len = 2;
else if ((c & 0xF0) == 0xE0) len = 3;
else if ((c & 0xF8) == 0xF0) len = 4;
else len = 1; // fallback
dots.possibleStrings.push_back(chars.substr(i, len));
i += len;
}
}

configPos = pos;
colorState.font = colorConfig.font;

Expand All @@ -87,13 +109,26 @@ void CPasswordInputField::configure(const std::unordered_map<std::string, std::a

pos = posFromHVAlign(viewport, size->goal(), configPos, halign, valign);

if (!dots.textFormat.empty()) {
if (!dots.possibleStrings.empty()) {
Hyprgraphics::CTextResource::STextResourceData request;
request.font = fontFamily;
request.color = colorConfig.font.asRGB();
request.fontSize = (int)(std::nearbyint(configSize.y * dots.size * 0.5f) * 2.f);
AWP<IWidget> widget(m_self);
for (const auto& s : dots.possibleStrings) {
request.text = s;
size_t id = g_asyncResourceManager->requestText(request, widget);
dots.stringResourceIDs[s] = id;
dots.resourceToString[id] = s;
}
} else if (!dots.textFormat.empty()) {
Hyprgraphics::CTextResource::STextResourceData request;
request.text = dots.textFormat;
request.font = fontFamily;
request.color = colorConfig.font.asRGB();
request.fontSize = (int)(std::nearbyint(configSize.y * dots.size * 0.5f) * 2.f);
dots.textResourceID = g_asyncResourceManager->requestText(request, nullptr);
AWP<IWidget> widget(m_self);
dots.textResourceID = g_asyncResourceManager->requestText(request, widget);
}

// request the inital placeholder asset
Expand All @@ -115,6 +150,22 @@ void CPasswordInputField::reset() {
placeholder.asset = nullptr;
placeholder.resourceID = 0;
placeholder.currentText.clear();

if (dots.textAsset)
g_asyncResourceManager->unload(dots.textAsset);

dots.textAsset = nullptr;
dots.textResourceID = 0;

for (auto& [s, asset] : dots.stringAssets) {
if (asset)
g_asyncResourceManager->unload(asset);
}
dots.stringAssets.clear();
dots.stringResourceIDs.clear();
dots.resourceToString.clear();
dots.possibleStrings.clear();
dots.currentRandomStrings.clear();
}

static void fadeOutCallback(AWP<CPasswordInputField> ref) {
Expand Down Expand Up @@ -170,8 +221,31 @@ void CPasswordInputField::updateDots() {
dots.currentAmount->setValueAndWarp(passwordLength);
else
*dots.currentAmount = passwordLength;
}

if (!dots.possibleStrings.empty()) {
if (dots.textChange) {
// reassign all on change
if (dots.currentRandomStrings.size() != passwordLength) {
dots.currentRandomStrings.resize(passwordLength);
for (size_t i = 0; i < passwordLength; ++i) {
dots.currentRandomStrings[i] = dots.possibleStrings[rand() % dots.possibleStrings.size()];
}
}
} else {
// stable: only add new, keep old
if (dots.currentRandomStrings.size() < passwordLength) {
size_t oldSize = dots.currentRandomStrings.size();
dots.currentRandomStrings.resize(passwordLength);
for (size_t i = oldSize; i < passwordLength; ++i) {
dots.currentRandomStrings[i] = dots.possibleStrings[rand() % dots.possibleStrings.size()];
}
} else if (dots.currentRandomStrings.size() > passwordLength) {
dots.currentRandomStrings.resize(passwordLength);
}
}
}

}
bool CPasswordInputField::draw(const SRenderData& data) {
if (firstRender || redrawShadow) {
firstRender = false;
Expand Down Expand Up @@ -238,7 +312,20 @@ bool CPasswordInputField::draw(const SRenderData& data) {
Vector2D passSize{RECTPASSSIZE, RECTPASSSIZE};
int passSpacing = std::floor(passSize.x * dots.spacing);

if (!dots.textFormat.empty()) {
if (!dots.possibleStrings.empty()) {
// Find the max size among all assets for consistent dot size
Vector2D maxSize = {0, 0};
for (auto& [s, asset] : dots.stringAssets) {
if (asset) {
maxSize.x = std::max(maxSize.x, asset->m_vSize.x);
maxSize.y = std::max(maxSize.y, asset->m_vSize.y);
}
}
if (maxSize.x > 0) {
passSize = maxSize;
passSpacing = std::floor(passSize.x * dots.spacing);
}
} else if (!dots.textFormat.empty()) {
if (!dots.textAsset)
dots.textAsset = g_asyncResourceManager->getAssetByID(dots.textResourceID);

Expand Down Expand Up @@ -284,7 +371,20 @@ bool CPasswordInputField::draw(const SRenderData& data) {

Vector2D dotPosition = inputFieldBox.pos() + Vector2D{xstart + (i * (passSize.x + passSpacing)), (inputFieldBox.h / 2.0) - (passSize.y / 2.0)};
CBox box{dotPosition, passSize};
if (!dots.textFormat.empty()) {
if (!dots.possibleStrings.empty()) {
std::string s = dots.currentRandomStrings[i];
auto it = dots.stringAssets.find(s);
if (it == dots.stringAssets.end() || !it->second) {
forceReload = true;
fontCol.a = DOTALPHA;
break;
}
// Center the asset in the box
Vector2D assetSize = it->second->m_vSize;
Vector2D centeredPos = box.pos() + (box.size() - assetSize) / 2.0;
CBox assetBox{centeredPos, assetSize};
g_pRenderer->renderTexture(assetBox, *it->second, fontCol.a, dots.rounding);
} else if (!dots.textFormat.empty()) {
if (!dots.textAsset) {
forceReload = true;
fontCol.a = DOTALPHA;
Expand Down Expand Up @@ -364,7 +464,9 @@ void CPasswordInputField::updatePlaceholder() {
}

void CPasswordInputField::onAssetUpdate(ResourceID id, ASP<CTexture> newAsset) {
;
if (dots.resourceToString.count(id)) {
dots.stringAssets[dots.resourceToString[id]] = newAsset;
}
}

void CPasswordInputField::updateWidth() {
Expand Down
7 changes: 7 additions & 0 deletions src/renderer/widgets/PasswordInputField.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <vector>
#include <any>
#include <unordered_map>
#include <map>

struct SPreloadedAsset;

Expand Down Expand Up @@ -70,6 +71,12 @@ class CPasswordInputField : public IWidget {
size_t textResourceID = 0;
std::string textFormat = "";
ASP<CTexture> textAsset = nullptr;
std::vector<std::string> possibleStrings;
std::map<std::string, size_t> stringResourceIDs;
std::map<std::string, ASP<CTexture>> stringAssets;
std::map<size_t, std::string> resourceToString;
std::vector<std::string> currentRandomStrings;
bool textChange = false;
} dots;

struct {
Expand Down